"use strict";

/**
	This constructor is used to create a guistep object.
	@constructor
*/
var guistep = function(){
	this.stepByStepMode = false;
};

/**
	This boolean attribute of guistep is used to tell if the step by step mode is turned on.
*/
guistep.stepByStepMode;

/**
	This number attribute of guistep is used to save the last step
*/
guistep.step;

/**
	This number attribute of guistep represents the amount of steps for the two sequences
*/
guistep.stepAmount;

/**
	This array of guistep is used to show only a part of the caculated array. With this the step by step
	mode is possible.
*/
guistep.shownArray;

/**
	This boolean attribute of guistep tells if the function nextLine is disabled.
*/
guistep.nextLineDisabled=false;

/**
	This boolean attribute of guistep tells if the function nextStep is disabled.
*/
guistep.nextStepDisabled=false;

/**
	This boolean attribute of guistep tells if the function prevLine is disabled.
*/
guistep.prevLineDisabled=true;

/**
	This boolean attribute of guistep tells if the function prevStep is disabled.
*/
guistep.prevStepDisabled=true;

/**
	This number attribute of guistep is used so the marking of wrong or right input of the user can be visualized.
*/
guistep.xMarked = -1;

/**
	This number attribute of guistep is used so the marking of wrong or right input of the user can be visualized.
*/
guistep.yMarked = -1;

/**
	This boolean attribute of guistep tells if the user input was wrong or right.
*/
guistep.rightValue = false;

/**
	This function is used to change from showing the complete result to use the step by step mode. Some preparation needs to
	be done and this si made with this function (i.e disable/enable sequence textfields or clear/fill the matrix).
*/
guistep.changeMode = function() {
	if(!guistep.stepByStepMode && ($("inputSeqA").value.length == 0
		|| $("inputSeqB").value.length == 0)){
		alert("Sequence A and B have to be setted first");
	}else{
		guistep.stepByStepMode = !guistep.stepByStepMode;
		if(guistep.stepByStepMode){
			gui.showBubble('topheadline', 'Control the step-by-step mode by clicking on the <b>arrow-buttons</b> in the box in the top-right-corner. ' +
						'Alternative, you can use the <b>arrow-keys</b> on your keyboard to control the steps.<br><br>' +
						'Optionally, you can <b>input the value of the next field</b> by yourself and it will be checked, by going to the next step.', 170, 300, true);
			this.step = 0;
			this.stepAmount = algorithm.array.length * algorithm.array[0].length + algorithm.showPath(gui.path);
			if(debug)console.log("stepAmount: " + this.stepAmount + "; pathAmount:" + algorithm.showPath(gui.path));
			$("modeControl").value="Complete Result";
			$("stepByStepControls").style.display="block";
			$("inputSeqA").disabled=true;
			$("inputSeqB").disabled=true;
			this.shownArray = new Array(algorithm.array.length);
			for (var i = 0; i < algorithm.array.length; i++) {
				this.shownArray[i] = new Array(algorithm.array[i].length);
				for(var j=0;j<algorithm.array[i].length; j++){
					this.shownArray[i][j] = new cellInfo();
				}
			}
			this.handleControlStyle();
			this.prepareShownArray();
			gui.showResult(algorithm.seqA, algorithm.seqB, this.shownArray, gui.path);
		}else{
			gui.hideBubble();
			$("modeControl").value="Start Step by Step";
			$("stepByStepControls").style.display="none";
			$("inputSeqA").disabled=false;
			$("inputSeqB").disabled=false;
			gui.showResult(algorithm.seqA, algorithm.seqB, algorithm.array, gui.path);
		}
	}
}

/**
	This function is used for the step by step mode to make one or more steps. Therefore the parameter call is used
	to give the direction.
	
	@param {string} call - 'next', 'nextLine', 'prevLine', 'prev' or nothing
	@param {dom element} butObj - is used to check, if the button is disabled
*/
guistep.stepCall = function(call, butObj) {
	if(typeof(butObj) != 'undefined' && butObj.className == 'disabled') {
		return false;
	}
	if(typeof(call) == 'undefined') {
		call = "";
		this.stepAmount = algorithm.array.length * algorithm.array[0].length + algorithm.showPath(gui.path);
		if(this.step>=this.stepAmount){
			this.step = this.stepAmount-1;
		}
	}
	gui.hideBubble();
	this.xMarked = -1;
	this.yMarked = -1;
	
	var shownArrayLength = this.shownArray.length * this.shownArray[0].length;
	if(debug)console.log("shownArrayLength: " + shownArrayLength);
	switch(call){
		case "next":
			this.step++;
			break;
		case "prev":
			if(typeof($('user_input')) != 'undefined' && $('user_input') != null){
				$('user_input').value = '';
			}
			this.step--;
			break;
		case "nextLine":
			if(typeof($('user_input')) != 'undefined' && $('user_input') != null){
				$('user_input').value = '';
			}
			if(this.step>=shownArrayLength){
				this.step = this.stepAmount-1;
			}else{
				var plusMaybe = 0;
				if(this.step % this.shownArray.length == 0){
					plusMaybe = 1;
				}
				this.step = (Math.floor(this.step/this.shownArray.length)+1)*(this.shownArray.length);
			}
			break;
		case "prevLine":
			if(typeof($('user_input')) != 'undefined' && $('user_input') != null){
				$('user_input').value = '';
			}
			if(this.step>shownArrayLength){
				this.step = shownArrayLength;
			}else{
				var minusMaybe = 0;
				if(this.step % this.shownArray.length == 0){
					minusMaybe = 1;
				}
				this.step = (Math.floor(this.step/this.shownArray.length)-minusMaybe)*(this.shownArray.length);
			}
			break;
	}
	this.handleControlStyle();
	this.prepareShownArray();
	if(debug)console.log("step: " + this.step);
	gui.showResult(algorithm.seqA, algorithm.seqB, this.shownArray, gui.path);
	if(this.xMarked>-1 && this.yMarked>-1){
		if(this.rightValue){
			gui.getResultCell(this.xMarked,this.yMarked).className = 'right';
		}else{
			gui.getResultCell(this.xMarked,this.yMarked).className = 'wrong';
		}
	}
	return false;
}

/**
	This function is used to handle the style of the step by step buttons.
*/
guistep.handleControlStyle = function(){
	if(this.step == this.stepAmount-1){
		$("nextLine").className='disabled';
		$("nextStep").className='disabled';
		$("prevLine").className='';
		$("prevStep").className='';
		this.nextLineDisabled = true;
		this.nextStepDisabled = true;
		this.prevLineDisabled = false;
		this.prevStepDisabled = false;
	}else if(this.step == 0){
		$("nextLine").className='';
		$("nextStep").className='';
		$("prevLine").className='disabled';
		$("prevStep").className='disabled';
		this.nextLineDisabled = false;
		this.nextStepDisabled = false;
		this.prevLineDisabled = true;
		this.prevStepDisabled = true;
	}else{
		$("nextLine").className='';
		$("nextStep").className='';
		$("prevLine").className='';
		$("prevStep").className='';
		this.nextLineDisabled = false;
		this.nextStepDisabled = false;
		this.prevLineDisabled = false;
		this.prevStepDisabled = false;
	}
}

/**
	This function is used to return the specific explanation for the bubble explaining a cell.
	
	@param {number} xPos - position x in the array
	@param {number} yPos - position y in the array
	@param {number} givenValue - value given by the user
*/
guistep.getCellExplanation = function(xPos, yPos, givenValue){
	var message;
	var height = 110;
	var width = 280;
	
	if(givenValue!=-1 && !$('infoControl').checked){
		this.xMarked = xPos;
		this.yMarked = yPos;
		this.rightValue = algorithm.array[yPos][xPos].value == givenValue;
	}
	
	if (xPos==0 && yPos==0){
		message = 'Initial Step. Value is always 0.';
		height = 80;
		width = 150;
	} else if (xPos==(algorithm.array[0].length-1) && yPos==(algorithm.array.length-1)){
		message = 'The Edit Distance adds up to the value <b>' + algorithm.array[yPos][xPos].value + '</b>. Also this is the end of every optimal alignment for these sequences.';
		height = 120;
		width = 200;
	} else {
		var celltext = '';
		var i = 0;
		var areequal = '';
		var addition = 0;
		var prevdiagonal = '';
		var prevvertical = '';
		var prevhorizontal = '';
		if (algorithm.array[yPos][xPos].horizontal==true){
			celltext+='horizontal';
			i++;
		}

		if (algorithm.array[yPos][xPos].vertical==true){
			if (i>0) celltext += ' and ';
			celltext+='vertical';
			i++;
		}
		if (algorithm.array[yPos][xPos].diagonal==true){ 
			if (i>0) celltext += ' and ';
			celltext+='diagonal';
			i++;
		}
		if (algorithm.seqA[yPos]==algorithm.seqB[xPos]){ 
			areequal+='equal'}
		else{
			areequal+='different';
			addition=1;
		}
		
		if ((yPos-1)>=0 &&(xPos-1)>=0){
			prevdiagonal='previous number from the diagonal cell (' + algorithm.array[yPos-1][xPos-1].value + ') the value <b>' +
						addition + '</b> is added and to the '
			height = 150;
			width = 280;
		}
		
		if (xPos-1>=0){
			prevvertical='vertical cell ('+algorithm.array[yPos][xPos-1].value+')'
			if (yPos-1>=0){
				prevvertical+=' and '
			}
		}
		
		if (yPos-1>=0){
			prevhorizontal='horizontal cell ('+algorithm.array[yPos-1][xPos].value+')'
		}
		
		message =  '<b>'+algorithm.seqA[yPos] +
						'</b> and <b>' + algorithm.seqB[xPos] +
						'</b> are <b>' + areequal +
						'</b>. To the ' + prevdiagonal + 'previous number from the ' +
						prevvertical + prevhorizontal + ' the value <b>1</b> is added and the minimal value (' +
						algorithm.array[yPos][xPos].value + ') is written into the cell. In this case the result is from the <b>' +
						celltext+'</b> cell.';
	}
	
	if (givenValue!=-1){
		message += '<br><br>';
		if(algorithm.array[yPos][xPos].value == givenValue){
			message+='<span class="right">Your input "' + givenValue + '" for this field was right!</span>';
			height += 20;
			
		}else{
			height += 40;
			message+='<span class="wrong">Your input "' + givenValue + '" for this field was wrong!</span><br>' +
						'<span class="right">Right value: ' + algorithm.array[yPos][xPos].value + '</span>';
		}
	}
					
	gui.showBubble(gui.getResultCell(xPos,yPos),message, height, width, true);
}

/**
	This function is used to return the specific explanation for the bubble explaining a part of the alignment.
	
	@param {number} xPos - position x in the matrix
	@param {number} yPos - position y in the matrix
	@param {number} nextXPos - following position x in the matrix
	@param {number} nextYPos - following position y in the matrix
*/
guistep.getPathExplanation = function(xPos, yPos, nextXPos, nextYPos){
	var message;
	var height = 100;
	var width = 150;
	if(xPos == 0 && yPos == 0){
		message = "This is the beginning of the viewed path and all other optimal alignment pathes for these sequences.";
	}else{
		var direction;
		var diagonal = false;
		var horizontal = false;
		var vertical = false;
		if(nextYPos == yPos-1){
			if(nextXPos == xPos-1){
				direction = "diagonal";
				diagonal = true;
			}else{
				direction = "horizontal";
				horizontal = true;
			}
		}else{
			direction = "vertical";
			vertical = true;
		}
		message = "This alignment passes this cell from <b>" + direction + "</b>.";
		var array = new Array();
		if(algorithm.array[yPos][xPos].horizontal && !horizontal){
			array.push("horizontal");
		}
		if(algorithm.array[yPos][xPos].vertical && !vertical){
			array.push("vertical");
		}
		if(algorithm.array[yPos][xPos].diagonal && !diagonal){
			array.push("diagonal");
		}
		if(array.length>0){
			var pathString;
			if(array.length==1){
				pathString = "direction is";
			}else{
				pathString = "directions are";
			}
			var directions = "";
			for(var i=0; i<array.length; i++){
				if(i!=0){
					directions += ", "
				}
				directions += array[i];
			}
			message += " Additionally " + array.length + " other " + pathString + " used by other pathes: <b>" + directions + "</b>."
		}
	}
	gui.showBubble(gui.getResultCell(xPos,yPos),message, height, width);
}

/**
	This function prepares a copy of the algorithm.array by including the current step.
*/
guistep.prepareShownArray = function(){
	var shownArrayLength = this.shownArray.length * this.shownArray[0].length;
	var counter = 0;
	if(this.step==0){
		this.shownArray[0][0].value = "$";
	}
	for(var i=0; i<this.shownArray[0].length; i++){
		for(var j=0; j<this.shownArray.length; j++){
			if(counter == this.step-1){
				if(this.step < shownArrayLength){
					this.shownArray[j][i] = new cellInfo();
					if((j+1)<this.shownArray.length){
						this.shownArray[j+1][i] = new cellInfo();
						this.shownArray[j+1][i].value = "$";
					}else{
						this.shownArray[0][i+1] = new cellInfo();
						this.shownArray[0][i+1].value = "$";
					}
				}
				this.getCellExplanation(i,j, this.getUserInput());
			}
			if(counter > this.step){
				this.shownArray[j][i] = new cellInfo();
			}else if(counter != this.step){
				this.shownArray[j][i].horizontal=algorithm.array[j][i].horizontal;
				this.shownArray[j][i].vertical=algorithm.array[j][i].vertical;
				this.shownArray[j][i].diagonal=algorithm.array[j][i].diagonal;
				this.shownArray[j][i].value =algorithm.array[j][i].value;
				this.shownArray[j][i].partOfPath = new Array();
			}
			counter++;
		}
	}
	
	counter = 0;
	var xPos = this.shownArray.length-1;
	var yPos = this.shownArray[0].length-1;
	var lastXPos = -1;
	var lastYPos = -1;
	for(var i=shownArrayLength; i<=this.step; i++){
		var lastXPos = xPos;
		var lastYPos = yPos;
		this.shownArray[xPos][yPos].partOfPath = algorithm.array[xPos][yPos].partOfPath;
		
		if(!(xPos==0 && yPos==0)){
			if(xPos>0 && yPos>0 && gui.cellIsPartOfPath(algorithm.array[xPos-1][yPos-1], gui.path)){
				xPos = xPos-1;
				yPos = yPos-1;
			}else if(xPos>0 && gui.cellIsPartOfPath(algorithm.array[xPos-1][yPos], gui.path)){
				xPos = xPos-1;
				yPos = yPos;
			}else{
				xPos = xPos;
				yPos = yPos-1;
			}
		}
	}
	
	if(lastXPos >=0){
		if(lastXPos == this.shownArray.length-1 && lastYPos == this.shownArray[0].length-1){
			this.getCellExplanation(lastYPos,lastXPos, this.getUserInput());
		}else{
			this.getPathExplanation(lastYPos,lastXPos, yPos, xPos);
		}	
	}
}

/**
	This function is used to allocate the input of the user.
*/
guistep.getUserInput=function(){
	var givenNumber;
	if(typeof($('user_input')) == 'undefined' || $('user_input') == null){
		givenNumber = -1
	}else{
		givenNumber = $('user_input').value;
		if(typeof(givenNumber) == 'undefined' || givenNumber == '' || givenNumber == NaN){
			givenNumber = -1;
		}
	}
	if(debug)console.log(givenNumber);
	return givenNumber;
}

window.addEventListener('keydown', function(event) {
	if(guistep.stepByStepMode){
		event = event || window.event;
		switch(event.keyCode){
			case 13: // enter
				if(!guistep.nextStepDisabled){
					guistep.stepCall('next');
				}
				break;
			case 37: //left arrow
				if(!guistep.prevStepDisabled){
					guistep.stepCall('prev');
				}
				break;
			case 38: // up arrow
				if(!guistep.prevLineDisabled){
					guistep.stepCall('prevLine');
				}
				break;
			case 39: // right arrow
				if(!guistep.nextStepDisabled){
					guistep.stepCall('next');
				}
				break;
			case 40: // down arrow
				if(!guistep.nextLineDisabled){
					guistep.stepCall('nextLine');
				}
				break;
			default:
				if(debug)console.log("Unknown key event!");
		}
	}
   return 
});